/** ==================================================================================== **/
/** 					 			GESTIONE LAYER : ol.layer							 **/
/** ==================================================================================== **/

/**
 * 
 *   	-->> 	 Direttiva ngJsTree 	<<--
 * Uso:
 *  (code)
 *  	<div js-tree="treeConfig" ng-model="treeData" should-apply="ignoreModelChanges()" tree="treeInstance" tree-events="ready:readyCB;create_node:createNodeCB"></div>
 *  (code)
 *  
 *  Parametri:
 * 
 * 		1. "treeConfig" : Questo è l'oggetto di configurazione del JsTree, se non vi fornirà uno, un vuoto verrà creato (non obbligatorio).
 * 
 *  	2. "treeData" : L'array con gli elementi della struttura, sarà utilizzato per l'associazione dei dati (aggiunta / rimozione / aggiornamento 
 *  			        di questi dati si rifletterà nella struttura).
 *  
 *  	3. "ignoreModelChanges()" : Un metodo che restituisce vero o falso. Se ritorna falso, modifiche del modello non si rifletteranno nella 
 *  							    struttura (non obbligatorio).
 *  
 *  	4. "treeInstance" : L'istanza JsTree verrà assegnato a questa variabile nel vostro ambito di controllo (non obbligatorio).
 *  
 *  	5. "ready:readyCB;create_node:createNodeCB" : Elenco degli eventi JsTree e le funzioni che verranno chiamati per ogni evento (non obbligatorio).
 *  
 *  Utilizzo dell'API JsTree dal controller:
 *  
 *  	Aggiungere l'attributo "tree" alla direttiva jstree e assegnargli un nome di una variabile che nello scope del controller verrà 
 *  	valorizzata con l'istanza jstree.
 *  
 *  	Esempio d'uso:  $scope.treeInstance.jstree(true).get_selected();
 *  
 *  
 *  	-->> 	 Uso Direttiva ngJsTree 	<<--
 *  
 *  Parametri utili al build dell'albero dei layer.
 * 
 *  	- "treeControl": controllo per di openlayers per "Mostrare e nascondere l'albero".
 *  	- "treeConfig": l'oggetto di configurazione del JsTree
 *  	- "treeData": l'array con gli elementi della struttura
 *  
 * Uso HTML Albero dei layer:
 *  (code)
 *    <!-- Albero dei layer -->
 *    <div class="areeTematiche" ng-class="ctrl.ngShowTree ? 'open' : ''" > 
 *    
 *    	<!-- Title -->
 *      <div class="areeTematiche_heading">
 *      	<span class="label">
 *      		{{ 'Mappa.areeTematiche' | translate }}
 *      		<i class="fa fa-times pointer close" ng-click="ctrl.ngShowTree=!ctrl.ngShowTree" title="{{'Mappa.closeTree.title' | translate}}"></i>
 *      	</span>
 *      </div>
 *      
 *      <!-- Ricerca layer -->
 *      <div class="input-group input-group-sm">
 *      	<input ng-model="ngTxtSearchTreeLayer" ng-keyup="ngSearchTreeLayer()" type="text" class="form-control" placeholder="{{'Mappa.ricerca.layer.placeholder' | translate}}" aria-describedby="clearLayoutTree_q" />
 *      	<span ng-click="ngClearSearchTreeLayer()" class="input-group-addon pointer" id="clearLayoutTree_q" title="{{'Mappa.clearSearch.title' | translate}}"><i class="fa fa-times"></i></span>
 *      </div>
 *      
 *      <!-- Albero -->
 *      <div js-tree="treeConfig" ng-model="treeData" should-apply="ignoreModelChanges()" tree="treeInstance"></div>
 *   </div>
 * (end)
 * 
 * Uso HTML del Control:
 *   (code)
 *  	<!-- Layer Tree control --> 
 *  	<div ngeo-control="treeControl" ngeo-control-map="olMap" 
 *  		 class="{{ctrl.ngShowTree ? 'pleft ol-selectable' : 'ol-unselectable'}} {{ngFulltools ? 'full-tools' : ''}}" 
 *  		 ng-click="ctrl.ngShowTree=!ctrl.ngShowTree"></div>
 *   (code)
 *   
 *  ========================================================================================
 *  -----------------------------       GOOGLE LAYER 		-------------------------------- 
 * 
 * URL TROVATE:
 * 
 * Hybrid - http://mt1.google.com/vt/lyrs=y&x=[x]&y=[y]&z=[z]
 * Satellite - http://khm1.google.com/kh/v=101&x=[x]&y=[y]&z=[z] 
 * Works for zoom level 13 and lower only.
 * Street - http://mt0.google.com/vt/x=[x]&y=[y]&z=[z]
 * Street - http://mt1.google.com/vt/lyrs=m&x=[x]&y=[y]&z=[z]
 * Terrain - http://mt1.google.com/vt/lyrs=p&x=[x]&y=[y]&z=[z]
 *	
 *
 * URL FUNZIONANTI:
 * 
 * Roadmap: http://mt0.google.com/vt/x&x={x}&y={y}&z={z}
 * Terrain: http://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}
 * Hybrid: http://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}
 * 
 * 
 * HTML ATTRIBUTION:
 * 
 * attributions: [
 * 			new ol.Attribution({ html: '© 2016 Google - Map data' }),
 * 			new ol.Attribution({ html: '<a href="https://developers.google.com/maps/terms">Terms of Use</a>' })
 * ]
 * attributions: [ 
 * 			new ol.Attribution({ html: '© Google' }),
 * 			new ol.Attribution({ html: '<a href="https://developers.google.com/maps/terms">Terms of Use.</a>' })
 * ]
 */
BootApplication.service("olLayerService", [ '$http', '$q', 'log', '$translate', 'LoadingAppService', 'DialogService', 'olStyleService', "configuration", "olFormatService",
                                          function( $http, $q, log, $translate, LoadingAppService, DialogService, olStyleService, appConstants, olFormatService ) {
	
	/**
	 * Bing maps - Type of imagery.
	 */
	var BingMaps_ImageryType = [ 'Road', 'Aerial', 'AerialWithLabels' ];

    /**
	 * Layer Bing Road (Roads without additional imagery.)
	 */
	this.createLayerBingRoad = function(){
		
		var bingRoad =
				new ol.layer.Tile({ 
		    		name: "Bing Road",
		    		source: new ol.source.BingMaps({
		    		     culture: "it-IT",
		    			 /** Bing Maps API key **/
		    			 key: appConstants.getParam("ol.ApplicationProperty").BING_MAPS_KEY,
		    			 /** Type of imagery. **/
		    			 imagerySet: BingMaps_ImageryType[0]
		    		}),
		    		title: "Bing Road",
		    		type: 'base',
					visible: false,
					displayInLayerSwitcher: true
				});
		
		return bingRoad;
	}// close createLayerBingRoad
	
	/**
	 * Layer Bing Aerial (Aerial imagery)
	 */
	this.createLayerBingAerial = function(){
		
		var bingAerial =
				new ol.layer.Tile({ 
					name: "Bing Aerial",
					source: new ol.source.BingMaps({
						  culture: "it-IT",
						  /** Bing Maps API key **/
						  key: appConstants.getParam("ol.ApplicationProperty").BING_MAPS_KEY,
						  /** Type of imagery. **/
						  imagerySet: BingMaps_ImageryType[1],
						  /** Whether to wrap the world horizontally **/
						  wrapX: false 
			    	}),
			    	title: "Bing Aerial",
			    	type: 'base',
					visible: false,
					displayInLayerSwitcher: true
		       });
		
		return bingAerial;
	}// close createLayerBingAerial
	
	/**
	 * Layer Bing AerialWithLabels (Aerial imagery with a road overlay.)
	 */
	this.createLayerBingAerialWithLabels = function(){
		
		var bingAerialWithLabels = 
				new ol.layer.Tile({ 
					name: "Bing AerialWithLabels",
					source: new ol.source.BingMaps({
	    		    	culture: "it-IT",
						/** Bing Maps API key **/
						key: appConstants.getParam("ol.ApplicationProperty").BING_MAPS_KEY,
						/** Type of imagery. **/
						imagerySet: BingMaps_ImageryType[2],
						/** Whether to wrap the world horizontally **/
						wrapX: false
					}),
					title: "Bing Aerial With Labels",
					type: 'base',
					visible: false,
					displayInLayerSwitcher: true
				});
		
		return bingAerialWithLabels;
	}// close createLayerBingAerialWithLabels
	
	/**
	 * Layer OSM Ligth
	 */
	this.createLayerOsmLigth = function(){
		var osm = 
				new ol.layer.Tile({
					name: "OsmLigth", 
	                source: new ol.source.XYZ({
	                    urls: [
	                       'https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png',
	                       'https://cartodb-basemaps-b.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png',
	                       'https://cartodb-basemaps-c.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png',
	                       'https://cartodb-basemaps-d.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png'
	                    ],
	                    attributions: [
	                        new ol.Attribution({
	                            html: [ '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; ' +
	                                    '<a href="https://carto.com/attributions">CARTO</a>'
	                                  ]
	                        })
	                    ]
	                }),
	                title: "Open Street Map (Light Version)",
	                type: 'base',
					visible: false,
					displayInLayerSwitcher: true
	            });
		
		return osm;
	}// close createLayerOpenStreetMap
	
	/**
	 * Layer OSM
	 */
	this.createLayerOpenStreetMap = function(){
		var osm = 
				new ol.layer.Tile({ 
		    		name: "Osm", 
		    		source: new ol.source.OSM({ /** Whether to wrap the world horizontally **/ wrapX: false }),
		    		title: "Open Street Map",
		    		type: 'base',
					visible: true,
					displayInLayerSwitcher: true
		    	});
		
		return osm;
	}// close createLayerOpenStreetMap
	
	/**
	 * Layer Google Satellite (displays Google Earth satellite images)
	 */
	this.createLayerGoogleSat = function(map){

		var googleSat = null; // mi manca l'url
//				new ol.layer.Tile({
//						name: "GoogleSat", 
//						source: new ol.source.XYZ({
//							url: "http://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}",
//							attributions: [
//							               new ol.Attribution({ html: 'Map data ©2016 Google' }),
//							               new ol.Attribution({ html: '<a href="https://www.google.com/intl/it-IT_US/help/terms_maps.html" target="_blank" style="text-decoration: none; cursor: pointer; color: rgb(68, 68, 68);">Termini e condizioni d\'uso</a>' })
//							              ]
//						}),
//					    title: "Google Satellite",
//					    type: 'base',
//					    visible: false,
//					    displayInLayerSwitcher: true
//				});
	
		return googleSat;
	}// close createLayerGoogleSat
	
	/**
	 * Layer Google Terrain (displays a physical map based on terrain information)
	 */
	this.createLayerGoogleTerrain = function(){
		
		var googleTerrain = 
				new ol.layer.Tile({
					name: "GoogleTerrain", 
					source: new ol.source.XYZ({
						url: "http://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}",
						attributions: [
						               new ol.Attribution({ html: 'Map data ©2016 Google' }),
						               new ol.Attribution({ html: '<a href="https://www.google.com/intl/it-IT_US/help/terms_maps.html" target="_blank" style="text-decoration: none; cursor: pointer; color: rgb(68, 68, 68);">Termini e condizioni d\'uso</a>' })
						              ]
					}),
				    title: "Google Terrain",
				    type: 'base',
				    visible: false,
				    displayInLayerSwitcher: true
				});		
		
		return googleTerrain;
	}// close createLayerGoogleTerrain
	
	/**
	 * Layer Google Hybrid (displays a mixture of normal and satellite views)
	 */
	this.createLayerGoogleHybrid = function(){
		
		var googleHybrid = 
				new ol.layer.Tile({
					name: "GoogleHybrid", 
					source: new ol.source.XYZ({
						url: "http://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}",
						attributions: [
						               new ol.Attribution({ html: 'Map data ©2016 Google' }),
						               new ol.Attribution({ html: '<a href="https://www.google.com/intl/it-IT_US/help/terms_maps.html" target="_blank" style="text-decoration: none; cursor: pointer; color: rgb(68, 68, 68);">Termini e condizioni d\'uso</a>' })
						              ]
					}),
				    title: "Google Hybrid",
				    type: 'base',
				    visible: false,
				    displayInLayerSwitcher: true
				});		
		
		return googleHybrid;
	}// close createLayerGoogleHybrid
	
	/**
	 * Layer Google RoadMap (displays the default road map view)
	 */
	this.createLayerGoogleRoadMap = function(){
		
		var googleRoadMap = 
				new ol.layer.Tile({
					name: "GoogleRoadmap", 
					source: new ol.source.XYZ({
						url: "http://mt0.google.com/vt/x&x={x}&y={y}&z={z}",
						attributions: [
						               new ol.Attribution({ html: 'Map data ©2016 Google' }),
						               new ol.Attribution({ html: '<a href="https://www.google.com/intl/it-IT_US/help/terms_maps.html" target="_blank" style="text-decoration: none; cursor: pointer; color: rgb(68, 68, 68);">Termini e condizioni d\'uso</a>' })
						              ]
					}),
				    title: "Google Roadmap",
				    type: 'base',
				    visible: false,
				    displayInLayerSwitcher: true
				});		
		
		return googleRoadMap;
	}// close createLayerGoogleRoadMap
	
	/**
	 * Layer Vuoto
	 */
	this.createLayerVuoto = function(){
		var vuoto = new ol.layer.Tile({ source: null, title: "Vuoto", type: 'base', visible: false, displayInLayerSwitcher: true });
		
		return vuoto;
	}// close createLayerVuoto
	
	/**
	 * Ritorna l'oggetto di configurazione del JsTree.
	 * 
	 * @return {@link Object}
	 */
	this.createTreeConfig = function (){
		/**
		 */
		var treeConfig = 
			{
				core: {
					error: function(error){
						log.error("Build tree ", error);
					},
					strings: {
						/*
						 * Configurare le varie stringhe utilizzate in tutto l'albero
						 * La chiave è la stringa da sostituire e il valore è la nostra sostituzione.
						 */
						"Loading ..." : $translate.instant("OpenLayers.loadingTree")
				    },
				    /* the open / close animation duration in milliseconds - set this to false to disable the animation (default is 200) **/
				    animation: 200,
				    /* a boolean indicating if multiple nodes can be selected **/
				    multiple: true,
				    /* Should the node should be toggled if the text is double clicked . Defaults to true */
				    dblclick_toggle: true
				},
				checkbox: {
					"keep_selected_style" : false
				},
				types: {
					default: {
						icon: "jstree-nothemeicon"
					}
				},
				search: {
					/* Indicates if the search should be fuzzy or not (should chnd3 match child node 3). Default is false. */
					fuzzy: false,
					/* Indicates if the search should be case sensitive. Default is false. */
					case_sensitive: false,
					/*
					 * Indica se l'albero dovrebbe essere filtrata (per impostazione predefinita) per mostrare i nodi solo corrispondenti 
					 * (tenere a mente questo può essere un pesante su grandi alberi vecchi browser).
					 * Questa impostazione può essere modificata in fase di esecuzione quando si chiama il metodo di ricerca. 
					 * Il valore predefinito è falso.
					 */
					show_only_matches: false,
					/*
					 * Indica se vengono visualizzate i figli di elemento abbinato (quando show_only_matches è vero)
					 * Questa impostazione può essere modificata in fase di esecuzione quando si chiama il metodo di ricerca. 
					 * Il valore predefinito è falso.
					 */
					show_only_matches_children: false,
					/*
					 * Indica se tutti i nodi aperti per rivelare il risultato della ricerca, devono essere chiusi quando la ricerca viene cancellato 
					 * o viene eseguita una nuova ricerca. Il valore predefinito è vero.
					 */
					close_opened_onclear: true,
					/*
					 * Indica se solo nodi foglia devono essere inclusi nei risultati di ricerca. Il valore predefinito è falso.
					 */
					search_leaves_only: false
				},
				plugins: [ "types", "checkbox", "search", "sort" ],
				sort: function (a, b) {
					return this.get_node(a).li_attr.ordine > this.get_node(b).li_attr.ordine ? 1 : -1;
				}
			};
		
		return treeConfig;
	}// close createTreeConfig
	
	/**
	 * Dati l'identificativo del profilo e della mappa, ritorna l'array con gli elementi della struttura da cui è composto il JsTree.
	 * 
	 * @param {@link MapOptions} options
	 * @return {@link Array}
	 */
	this.createTreeData = function (options) {
		try{
			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.default")); // Loader.buildLayer
			 
			/**
			 * Restituisce il json con il quale disegnare l'albero(Categoria e sottocategoria) 
			 * 
			 * Da RICORDARE che l'id profilo e l'id mappa sono sostituiti all'url facendo il replace delle sequenti stringhe:
			 *   - {value1} per l'id profilo.
			 *   - {value2} per l'id mappa.
			 * 
			 * Es url: "/getTreeCategoria.json/{value1}/{value2}"
			 * */
			var url = options.getTreeUrl();
				/** replace dei parametri **/
			    url = appConstants.getParam("Constants").ContextPath + url + "/" + options.getProfiloId() + "/" + options.getMappaId();
				
			var deferred = $q.defer();
			
			$http({
				method: 'GET',
				url: url,
				responseType: "json",
				cache: false,
			})
			.success(function(data, status, headers, config) {
				if(data != null && angular.isArray(data)){
					
					// spengo il loading
					LoadingAppService.hideLoader();
					
					deferred.resolve(data, status);
				}else{
					/**
					 * La procedura che costruisce l'alabero dei layer, apro una popup informativa dell'errore.
					 */
					log.error("Error build tree ", data);
					/**
		    		 * spengo il loading
		    		 */
					LoadingAppService.hideLoader();
					/**
					 * E' andata in errore la chiamata al server, apro una popup informativa dell'errore.
					 */
					DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));
					
					deferred.reject(data, status);
				}
			})
			.error(function(data, status, headers, config) {
				/**
				 * La procedura che costruisce l'alabero dei layer, apro una popup informativa dell'errore.
				 */
				log.error("Error build tree ", data);
				/**
	    		 * spengo il loading
	    		 */
				LoadingAppService.hideLoader();
				/**
				 * E' andata in errore la chiamata al server, apro una popup informativa dell'errore.
				 */
				DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));
				
				deferred.reject(data, status);
			});
		}catch(e){
			 /**
			  * La procedura che costruisce l'albero dei layer, apro una popup informativa dell'errore.
			  */
			 log.error("Error build tree " + e, e.stack);
			 /**
			  * spengo il loading
			  */
			 LoadingAppService.hideLoader();
			 /**
			  *  apri popup errore
			  */
			 DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));

			 deferred.reject(null, status);
		}

		return deferred.promise;
	}// close createTreeData
	
	/**
	 * Dati l'identificativo del profilo e della mappa, ritorna il json con l'elenco dei layer associati alla mappa di questo profilo.
	 * 
	 * @param {@link MapOptions} options
	 * 
	 * @return {@link Array}
	 */
	this.createTreeLayerInfo = function (options) {
		try{
			var deferred = $q.defer();

			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.default")); // Loader.buildLayer
			 
			/**
			 * Da RICORDARE che l'id profilo e l'id mappa sono sostituiti all'url facendo il replace delle sequenti stringhe:
			 *   - {value1} per l'id profilo.
			 *   - {value2} per l'id mappa.
			 * 
			 * Es url: "/getLayersInfo.json/{value1}/{value2}"
			 * */
			var url = options.getLayerUrl();
				/** replace dei parametri **/
			    url = appConstants.getParam("Constants").ContextPath + url + "/" + options.getProfiloId() + "/" + options.getMappaId();
			    
			$http({
				method: 'GET',
				url: url,
				responseType: "json",
				cache: false,
			})
			.success(function(response, status, headers, config) {
				if(response != null && response.success && angular.isObject(response.aaData)){
					
					// spengo il loading
					LoadingAppService.hideLoader();
					
					deferred.resolve(response.aaData, status);
				}else{
					/**
					 * La procedura che recupera l'elenco dei layer associati alla mappa, apro una popup informativa dell'errore.
					 */
					log.error("Error get list layer map ", response);
					/**
		    		 * spengo il loading
		    		 */
					LoadingAppService.hideLoader();
					/**
					 * E' andata in errore la chiamata al server, apro una popup informativa dell'errore.
					 */
					DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));
					
					deferred.reject(response, status);
				}
			})
			.error(function(data, status, headers, config) {
				/**
				 * La procedura che recupera l'elenco dei layer associati alla mappa, apro una popup informativa dell'errore.
				 */
				log.error("Error get list layer map ", data);
				/**
	    		 * spengo il loading
	    		 */
				LoadingAppService.hideLoader();
				/**
				 * E' andata in errore la chiamata al server, apro una popup informativa dell'errore.
				 */
				DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));
				
				deferred.reject(data, status);
			});
		}catch(e){
			 /**
			  * La procedura che recupera l'elenco dei layer associati alla mappa, apro una popup informativa dell'errore.
			  */
			 log.error("Error get list layer map " + e, e.stack);
			 /**
			  * spengo il loading
			  */
			 LoadingAppService.hideLoader();
			 /**
			  *  apri popup errore
			  */
			 DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));

			 deferred.reject(null, status);
		}

		return deferred.promise;
	}// close createTreeLayerInfo
	
	/**
	 * Dati l'identificativo del profilo e della mappa, ritorna il json con l'elenco degli stili dei layer associati alla mappa di questo profilo.
	 * 
	 * @param {@link MapOptions} options
	 * 
	 * @return {@link Array}
	 */
	this.createTreeStyles = function (options) {
		try{
			var deferred = $q.defer();

			// loading ..
			LoadingAppService.showLoader($translate.instant("Loader.default")); // Loader.buildLayer
			 
			/**
			 * Da RICORDARE che l'id profilo e l'id mappa sono sostituiti all'url facendo il replace delle sequenti stringhe:
			 *   - {value1} per l'id profilo.
			 *   - {value2} per l'id mappa.
			 * 
			 * Es url: "/treeLayers.json/{value1}/{value2}"
			 */
			var url = options.getLayerStyleUrl();
				/** replace dei parametri **/
			    url = appConstants.getParam("Constants").ContextPath + url + "/" + options.getProfiloId() + "/" + options.getMappaId();
			    
			$http({
				method: 'GET',
				url: url,
//				responseType: "json",
				cache: false,
				contentType: "application/x-javascript;charset=utf-8",
			})
			.success(function(response, status, headers, config) {
				if(response != null && response.success && !angular.equals(response.aaData, "")){
					
					// spengo il loading
					LoadingAppService.hideLoader();
					
					deferred.resolve(response.aaData, status);
				}else{
					/**
					 * La procedura che recupera l'elenco dei layer associati alla mappa, apro una popup informativa dell'errore.
					 */
					log.error("Error get list style layer ", response);
					/**
		    		 * spengo il loading
		    		 */
					LoadingAppService.hideLoader();
					/**
					 * E' andata in errore la chiamata al server, apro una popup informativa dell'errore.
					 */
					DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));
					
					deferred.reject(response, status);
				}
			})
			.error(function(data, status, headers, config) {
				/**
				 * La procedura che recupera l'elenco dei layer associati alla mappa, apro una popup informativa dell'errore.
				 */
				log.error("Error get list style layer ", data);
				/**
	    		 * spengo il loading
	    		 */
				LoadingAppService.hideLoader();
				/**
				 * E' andata in errore la chiamata al server, apro una popup informativa dell'errore.
				 */
				DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));
				
				deferred.reject(data, status);
			});
		}catch(e){
			 /**
			  * La procedura che recupera l'elenco dei layer associati alla mappa, apro una popup informativa dell'errore.
			  */
			 log.error("Error get list style layer " + e, e.stack);
			 /**
			  * spengo il loading
			  */
			 LoadingAppService.hideLoader();
			 /**
			  *  apri popup errore
			  */
			 DialogService.showErrorAlertApplication($translate.instant('Init.build.tree.error'));

			 deferred.reject(null, status);
		}

		return deferred.promise;
	}// close createTreeStyles
	
	/**
	 * Ritorna il layer vector appositamente configurato per la visualizzazione su mappa
	 * della posizione gelocalizzata dell'utente.
	 * 
	 * Returns:  
	 * 	  {@link ol.layer.Vector} Vector | null. 
	 */
	this.createLayerVectorToGeolocate = function (){
		
		/** **/
		var vector = new ol.layer.Vector({
				/** The bounding extent for layer rendering. The layer will not be rendered outside of this extent. **/
//				extent: olFormatService.olProjTransformExtent(options.getExtent(), options.getSourceProjection(), options.getDestProjection()),
				/** **/
			    source: new ol.source.Vector({ features: [], /** Whether to wrap the world horizontally **/ wrapX: false }),
			    /** The feature style. ol.style.Style **/
				style: olStyleService.createStyleFeatureGeolocate(),
				/** Nome del layer **/
				name: appConstants.getParam("ol.ApplicationProperty").NAME_LAYER_GEOLOCATION
			});
		
		return vector;
	}// close createLayerVectorToGeolocate
	
	/**
	 * Ritorna il layer vector appositamente configurato per la visualizzazione su mappa
	 * delle feature prodotte dagli strumenti di disegno.
	 * 
	 * @param {@link ol.style.Style} style
	 * 
	 * Returns:  
	 * 	  {@link ol.layer.Vector} Vector | null. 
	 */
	this.createLayerVectorToDrawFeature = function (style){
		
		/** **/
		var vector = new ol.layer.Vector({
				/** **/
			    source: new ol.source.Vector({ features: [], /** Whether to wrap the world horizontally **/ wrapX: false }),
			    /** The feature style. ol.style.Style **/
				style: style != null ? style : olStyleService.createStyleFeatureDraw(),
				/** Nome del layer **/
				name: appConstants.getParam("ol.ApplicationProperty").NAME_LAYER_DRAW_FEATURE
			});
		
		return vector;
	}// close createLayerVectorToDrawFeature
	
	/**
	 * Ritorna il layer vector con una configurazione di default.
	 * 
	 * @param {@link String} layerName, nome del layer
	 * @param {@link ol.style.Style} style, stile delle feature del layer
	 * 
	 * Returns:  
	 * 	  {@link ol.layer.Vector} Vector | null. 
	 */
	this.createLayerVector = function (layerName, style){
		
		/** **/
		var vector =  new ol.layer.Vector({
	        	/** The feature style. ol.style.Style | Array.<ol.style.Style> | ol.FeatureStyleFunction **/
	    		style: style != null ? style : olStyleService.createStyleDefault(),
	    		/** **/
	            source: new ol.source.Vector({ features: [], /** Whether to wrap the world horizontally **/ wrapX: false }),
	            /** Nome del layer **/
	            name: layerName
	    	});
		
		return vector;
	}// close createLayerVector
	
	/**
	 * Ritorna il layer vector configurato per l'albero dei layer .
	 * 
	 * @param {@link ol.Map} map, instance map
	 * @param {@link Object} layerDTO, oggetto di configurazione del layer
	 * @param {@link Locale} locale, language
	 * @param {@link String} styleFuction
	 * @param {@link ol.ProjectionLike} sourceProj
	 * @param {@link ol.ProjectionLike} destProj
	 * 
	 * Returns:  
	 * 	  {@link ol.layer.Vector} Vector | null. 
	 */
	this.createOlLayerVectorFromTree = function (map, layerDTO, locale, styleFuction, sourceProj, destProj){
		/**
		 * Dato il codice del formato delle feature, ritorna il corrispondente ol.format.Feature
		 */
		var format = olFormatService.trasformInOlFormat(layerDTO.source.format);
		/**
		 * Dato il codice del formato delle feature, ritorna il corrispondente ol.loadingstrategy
		 */
		var strategy = olFormatService.transformInOlLoadingStrategy(layerDTO.source.strategy);
		
		/** **/
		var url = appConstants.getParam("Constants").USE_PROXY + layerDTO.source.url;
		//TODO remove post demo
		if(layerDTO.codice == "tree_traffic_realtime"){
			url = layerDTO.source.url;
		}	
		/**
		 * Controllo se la request è di tipo WFS, se si devo aggiungere parametri all'url 
		 */
		if(layerDTO.source.wfs != null){
			/* controllo qual'è l'operatore da usare */
			url += (layerDTO.source.url.indexOf("?") == -1 ? "?" : "&");
			
			url += /* */
				   "service=" + layerDTO.source.wfs.service +
				   /* Service version, numero di versione corrente. Deve essere fornito il numero di versione completo. */
				   "&version=" + layerDTO.source.wfs.version +
				   /* Nome dell'operazione */
				   "&request=" + layerDTO.source.wfs.request +
				   /* Nome della feature type da descrivere (namespace:featuretype) */
				   "&typeName=" + layerDTO.source.wfs.typeName +
				   /* Formato in cui sono ritornate le feature*/
				   "&outputFormat=" + layerDTO.source.wfs.outputFormat;
			
			/* Numero di feature che devono essere restituite al massimo */
			if(layerDTO.source.wfs.maxFeatures != null) url += "&maxFeatures=" + layerDTO.source.wfs.maxFeatures;
			
			/**
			 * Use a ol.FeatureUrlFunction to generate the url with other loading strategies.  
			 */
			if(!angular.equals(layerDTO.source.strategy, "ALL")){
				var url_ = url;
				/**
				 * ol.source.Vector sources use a function of this type to get the url to load features from.
				 * 
				 * This function takes an ol.Extent representing the area to be loaded, a {number} representing 
				 * the resolution (map units per pixel) and an ol.proj.Projection for the projection as arguments and returns a {string} 
				 * representing the URL. 
				 */
				url = function(extent) { 
					 	 var ext = olFormatService.olProjTransformExtent(extent, destProj, sourceProj);
					  	 
					 	 return url_ + "&bbox=" + ext.join(','); 
					  };
			}
		}// close WFS
		else{
			/**
			 * Use a ol.FeatureUrlFunction to generate the url with other loading strategies.  
			 */
			if(!angular.equals(layerDTO.source.strategy, "ALL")){
				var url_ = url;
				/**
				 * ol.source.Vector sources use a function of this type to get the url to load features from.
				 * 
				 * This function takes an ol.Extent representing the area to be loaded, a {number} representing 
				 * the resolution (map units per pixel) and an ol.proj.Projection for the projection as arguments and returns a {string} 
				 * representing the URL. 
				 */
				url = function(extent) { 
					     var ext = olFormatService.olProjTransformExtent(extent, destProj, sourceProj);
					     
				 		 return url_ + (layerDTO.source.url.indexOf("?") == -1 ? "?" : "&") + 'bbox=' + ext.join(','); 
					  };
			}
		}// close else not wfs
		
		
		/**
		 * Provides a source of features for vector layers. Vector features provided by this source are suitable for editing. 
		 * See ol.source.VectorTile for vector data that is optimized for rendering.
		 */
		var source = 
				new ol.source.Vector({ 
					/** Features */
					features: [], 
					/** The feature format used by the XHR feature loader when url is set. Required if url is set, otherwise ignored. Default is undefined. */
					format: format,
					/** The loading strategy to use. By default an ol.loadingstrategy.all strategy is used, a one-off strategy which loads all features at once. */
					strategy: strategy,
					/**
					 * Use a string and an ol.loadingstrategy.all for a one-off download of all features from the given URL. 
					 * Use a ol.FeatureUrlFunction to generate the url with other loading strategies.  
					 */
					url: url,
			        /** 
			         * By default, an RTree is used as spatial index. When features are removed and added frequently, and the total number of features is low, setting this to false may improve performance. 
			         */
			        useSpatialIndex: layerDTO.source.useSpatialIndex,
					/**
					 * Wrap the world horizontally. Default is true. For vector editing across the -180° and 180° meridians to work properly, this should be set to false. The resulting geometry coordinates 
					 * will then exceed the world bounds.
					 */ 
			        wrapX: layerDTO.source.wrapX,
			        /** Attributions **/
					attributions: (layerDTO.source.attributions == null) ? null : new ol.Attribution({ html: layerDTO.source.attributions })
				});
		
		/**
		 * Controllo se il layer deve essere clusterizzato
		 */
		if(layerDTO.source.hasCluster){
			/** 
			 * Layer source to cluster vector data. Works out of the box with point geometries. For other geometry types, or if not all geometries
			 * should be considered for clustering, a custom geometryFunction can be defined.
			 */
			var sourceCluster = new ol.source.Cluster({
				/** Minimum distance in pixels between clusters. Default is 20. */
				distance: layerDTO.source.distance,
				/** Source. */
				source: source
			});
			
			source = sourceCluster;
		}// close ha cluster
		

		/** **/
		var vector =  new ol.layer.Vector({
			/** Opacity. 0-1. Default is 1. */
			opacity: layerDTO.opacity,
	    	/** Source. Required. */
	        source: source,
	        /** */
	        style: function (feature){
	        	/** Eval function style */
	    		var s = eval(styleFuction);
				return eval(layerDTO.codice)(feature);
			},
	        /**
	         * When set to true, feature batches will be recreated during animations. This means that no vectors will be shown clipped, but 
	         * the setting will have a performance impact for large amounts of vector data. When set to false, batches will be recreated when no 
	         * animation is active. Default is false.
	         */
	        updateWhileAnimating: layerDTO.updateWhileAnimating,
	        /**
	         * When set to true, feature batches will be recreated during interactions. See also updateWhileAnimating. Default is false.
	         */
	        updateWhileInteracting: layerDTO.updateWhileInteracting,
	        /** Visibility. */
	        visible: true,
	        /** 
	         * The z-index for layer rendering. At rendering time, the layers will be ordered, first by Z-index and then by position. 
	         * The default Z-index is 0. 
	         */
	        zIndex: layerDTO.zIndex,
	        /** Nome del layer */
	        name: layerDTO.codice,
	        /** */
	        title: layerDTO.label[locale.toUpperCase()],
	        /** */
	        otherParams: layerDTO.otherParams,
			/** */
			displayInLayerSwitcher: true
	    });
		
		return vector;
	}// close createOlLayerVectorFromTree
	
	/**
	 * Ritorna il layer wms per l'albero dei layer .
	 * 
	 * @param {@link Object} layerDTO, oggetto di configurazione del layer
	 * @param {@link Locale} locale, language
	 * 
	 * Returns:  
	 * 	  {@link ol.layer.Image} Image | null. 
	 */
	this.createOlLayerImageWMSFromTree = function (layerDTO, locale){
		
		/**
		 * Source for WMS servers providing single, untiled images.
		 */
		var source = new ol.source.ImageWMS({
			/** Attributions **/
			attributions: (layerDTO.source.attributions == null) ? null : new ol.Attribution({ html: layerDTO.source.attributions }),
			/** The type of the remote WMS server: mapserver, geoserver or qgis. Only needed if hidpi is true. Default is undefined. */
			serverType: layerDTO.source.serverType,
			/**
			 * WMS request parameters. At least a LAYERS param is required. STYLES is '' by default. VERSION is 1.3.0 by default. 
			 * WIDTH, HEIGHT, BBOX and CRS (SRS for WMS version < 1.3.0) will be set dynamically. Required.
			 */
			params: layerDTO.source.params,
			/**
			 * WMS service URL.
			 */
			url: appConstants.getParam("Constants").USE_PROXY + layerDTO.source.url + "?"
		});
		/**
		 * Server-rendered images that are available for arbitrary extents and resolutions. Note that any property set in the options 
		 * is set as a ol.Object property on the layer object; for example, setting title: 'My Title' in the options means that title is observable, 
		 * and has get/set accessors.
		 */
		var imageWms = new ol.layer.Image({
			/** Opacity. 0-1. Default is 1. */
			opacity: layerDTO.opacity,
	    	/** Source. Required. */
	        source: source,
	        /** Visibility. */
	        visible: true,
	        /** 
	         * The z-index for layer rendering. At rendering time, the layers will be ordered, first by Z-index and then by position. 
	         * The default Z-index is 0. 
	         */
	        zIndex: layerDTO.zIndex,
	        /** Nome del layer */
	        name: layerDTO.codice,
	        /** */
	        title: layerDTO.label[locale.toUpperCase()],
	        /** */
	        otherParams: layerDTO.otherParams,
			/** */
			displayInLayerSwitcher: true
	    });
		
		return imageWms;
	}// close createOlLayerImageWMSFromTree
	
	/**
	 * Ritorna il layer tile per l'albero dei layer .
	 * 
	 * @param {@link Object} layerDTO, oggetto di configurazione del layer
	 * @param {@link Locale} locale, language
	 * 
	 * Returns:  
	 * 	  {@link ol.layer.Tile} Tile | null. 
	 */
	this.createOlLayerTileWMSFromTree = function (layerDTO, locale){
		
		/**
		 * Layer source for tile data from WMS servers.
		 */
		var source = ol.source.TileWMS({
			/** Attributions **/
			attributions: (layerDTO.source.attributions == null) ? null : new ol.Attribution({ html: layerDTO.source.attributions }),
			/** The type of the remote WMS server: mapserver, geoserver or qgis. Only needed if hidpi is true. Default is undefined. */
			serverType: layerDTO.source.serverType,
			/**
			 * WMS request parameters. At least a LAYERS param is required. STYLES is '' by default. VERSION is 1.3.0 by default. 
			 * WIDTH, HEIGHT, BBOX and CRS (SRS for WMS version < 1.3.0) will be set dynamically. Required.
			 */
			params: layerDTO.source.params,
			/**
			 * WMS service URL.
			 */
			url: layerDTO.source.url
		});
		
		/**
		 * For layer sources that provide pre-rendered, tiled images in grids that are organized by zoom levels for specific resolutions. 
		 * Note that any property set in the options is set as a ol.Object property on the layer object; for example, setting title: 'My Title' in 
		 * the options means that title is observable, and has get/set accessors.
		 */
		var tileWms = new ol.layer.Tile({
			/** Opacity. 0-1. Default is 1. */
			opacity: layerDTO.opacity,
	    	/** Source. Required. */
	        source: source,
	        /** Visibility. */
	        visible: true,
	        /** 
	         * The z-index for layer rendering. At rendering time, the layers will be ordered, first by Z-index and then by position. 
	         * The default Z-index is 0. 
	         */
	        zIndex: layerDTO.zIndex,
	        /** Nome del layer */
	        name: layerDTO.codice,
	        /** */
	        title: layerDTO.label[locale.toUpperCase()],
	        /** */
	        otherParams: layerDTO.otherParams,
			/** */
			displayInLayerSwitcher: true
	    });
		
		return tileWms;
	}// close createOlLayerTileWMSFromTree
	
	/**
	 * Se _sourceProjection non è valorizzato restituisce valore da appConstants
	 */	
	this.getSourceProjection = function(_sourceProjection){
		return ! _sourceProjection ? appConstants.getParam("Constants")['SOURCE_PROJECTION'] : _sourceProjection;
	}
	
	/**
	 * Se _destProjection non è valorizzato restituisce valore da appConstants
	 */	
	this.getDestProjection = function(_destProjection){
		return ! _destProjection ? appConstants.getParam("Constants")['DEST_PROJECTION'] : _destProjection;
	}
	
	/**
	 * Costruisce un Open Layer dalle coordinate di un punto
	 * 
	 * @param _longitudine     : longitudine del punto
	 * @param _latitudine      : latitudine del punto
	 * @param _sourceProjection: latitudine del punto. Se non valorizzato si intende SOURCE_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @param _style           : style da applicare al layer. Se non valorizzato si intende olStyleService.createStyleFeatureGeolocate
	 * @param _zIndex          : zIndex da applicare al punto. Se non valorizzato si intende 1.
	 */	
	this.getPointLayer = function(_longitudine, _latitudine, _sourceProjection, _destProjection, _style ,_zIndex ){
		
		if(!_zIndex) _zIndex = 1;
		if(!_style) _style = olStyleService.createStyleFeatureGeolocate();

		var _sourceProjection = this.getSourceProjection(_sourceProjection);
		var _destProjection   = this.getDestProjection(_destProjection);
		
		var _longitudine = parseFloat(_longitudine);
		var _latitudine  = parseFloat(_latitudine );
		
		var _coordinate   = olFormatService.olProjTransorm(_longitudine, _latitudine, _sourceProjection, _destProjection);
		var _point        = olFormatService.olCoordinateToPoint(_coordinate);
		var _vectorSource = new ol.source.Vector();
		var _pointFeature = new ol.Feature({geometry: _point});
		var _layer        = new ol.layer.Vector({source: _vectorSource});
	    
	    _vectorSource.addFeature(_pointFeature);
	    _pointFeature.setStyle(_style);
	    _layer.setZIndex(_zIndex);
	    
	    return _layer;
    }//Close getPointLayer
	
	/**
	 * Costruisce un Open Layer dalle coordinate di un punto
	 * 
	 * @param _wkt             : _wkt string del punto
	 * @param _sourceProjection: latitudine del punto. Se non valorizzato si intende SOURCE_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @param _style           : style da applicare al layer. Se non valorizzato si intende olStyleService.createStyleFeatureGeolocate
	 * @param _zIndex          : zIndex da applicare al punto. Se non valorizzato si intende 1.
	 */	
    this.getPointLayerFromWKT = function(_wkt, _sourceProjection, _destProjection, _style, _zIndex){                                  
    	var _cooridnate = olFormatService.getPointCoordinateFromWKT(_wkt);
    	
    	return this.getPointLayer(_cooridnate[0], _cooridnate[1], _destProjection, _style, _zIndex);
    }//Close getPointLayerFromWKT
    
	/**
	 * @param _longitudine     : longitudine centro cerchio
	 * @param _latitudine      : latitudine centro cerchio
	 * @param _raggio          : raggio in metri del cerchio
	 * @param _sourceProjection: latitudine del punto. Se non valorizzato si intende SOURCE_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @retun ol.geom.Circle instance
	 */    
    this.getCircle = function(_longitudine, _latitudine, _raggio, _sourceProjection,_destProjection){
    	var _sourceProjection = this.getSourceProjection(_sourceProjection);
    	var _destProjection   = this.getDestProjection(_destProjection);
    	var _coordinate       = olFormatService.olProjTransorm(_longitudine, _latitudine, _sourceProjection, _destProjection);
    	return new ol.geom.Circle(_coordinate,_raggio);
    }//END getCircle
    
	/**
	 * Costruisce layer per cerchio
	 * @param _longitudine     : longitudine centro cerchio
	 * @param _latitudine      : latitudine centro cerchio
	 * @param _raggio          : raggio in metri del cerchio
	 * @param _sourceProjection: latitudine del punto. Se non valorizzato si intende SOURCE_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @param _style           : Style da applicare.  Se non valorizzato utilizza olStyleService.createCirconferenzaStyle
	 * @param _zIndex          : livello _zIndex da applicare al layer. Se non valorizzato si imposta ad 1
	 * @retun layer instance
	 */    
    this.getCircleLayer = function(_longitudine, _latitudine, _raggio, _sourceProjection, _destProjection, _style, _zIndex){
    	var _circle        = this.getCircle(_longitudine, _latitudine, _raggio, _sourceProjection, _destProjection);
    	var _circleFeature = new ol.Feature(_circle);
    	var _vectorSource  = new ol.source.Vector({projection: this.getSourceProjection(_sourceProjection),});

    	_vectorSource.addFeature(_circleFeature);

    	var _layer = new ol.layer.Vector({source: _vectorSource, style : [!_style ? olStyleService.createCirconferenzaStyle() : _style] });
    		_layer.setZIndex(!_zIndex ? 1 : _zIndex);
    	return _layer;
    }//End getCircleLayer
    
	/**
	 * Costruisce layer per cerchio
	 * @param _wktString       : _wktString che rappresenta il centro del cerchio
	 * @param _raggio          : raggio in metri del cerchio
	 * @param _sourceProjection: latitudine del punto. Se non valorizzato si intende SOURCE_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @param _style           : Style da applicare.  Se non valorizzato utilizza olStyleService.createCirconferenzaStyle
	 * @param _zIndex          : livello _zIndex da applicare al layer. Se non valorizzato si imposta ad 1
	 * @retun layer instance
	 */    
    this.getCircleLayerFromWKT = function(_wktString, _raggio, _sourceProjection, _destProjection, _style, _zIndex){
    	var _coordinates   = olFormatService.getPointCoordinateFromWKT(_wktString);
    	
    	return this.getCircleLayer(_coordinates[0] ,_coordinates[1], _raggio, _sourceProjection, _destProjection, _style, _zIndex);
    }//End getCircleLayerFromWKT
    
	/**
	 * Costruisce layer per polygon
	 * @param _wktString       : _wktString che rappresenta Polygon
	 * @param _sourceProjection: latitudine del punto. Se non valorizzato si intende SOURCE_PROJECTION in appConstants.js
	 * @param _destProjection  : latitudine del punto. Se non valorizzato si intende DEST_PROJECTION in appConstants.js
	 * @param _style           : Style da applicare.  Se non valorizzato utilizza createDefaultPolygonStyle
	 * @param _zIndex          : livello _zIndex da applicare al layer. Se non valorizzato si imposta ad 1
	 * @retun layer instance
	 */
    this.getPolygonLayer = function(_wktString, _sourceProjection, _destProjection, _style, _zIndex){
    	var _polygonFeature = olFormatService.olReadFeatureToWkt(_wktString, _sourceProjection, _destProjection);
    	var _vectorSource   = new ol.source.Vector();

    		_vectorSource.addFeature(_polygonFeature);

    	var _layer = new ol.layer.Vector({ source: _vectorSource, style : [!_style ? olStyleService.createDefaultPolygonStyle() : _style] });
    	
    		_layer.setZIndex(!_zIndex ? 1 : _zIndex);
    	
    	return _layer;
    }// close getPolygonLayer
}]); 
